/*
 * Justin Prentice
 * TCSS 305 (winter quarter)
 * Tetris program - Tetris Board test
 */

package board;

import org.junit.Before;
import org.junit.Test;

import piece.AbstractPiece;
import piece.IPiece;
import static org.junit.Assert.*;

/**
 * Testing class for the TetrisBoard class.
 * @author Justin Prentice
 * @version 1.0
 */
public class TetrisBoardTest
{
  /**
   * A tetris board for testing.
   */
  private TetrisBoard my_test_board;
  
  /**
   * Initializes a board with 30 iPieces in queue. 
   * @throws CloneNotSupportedException throws exception if cloning is not supported.
   */
  @Before
  public void initialize() throws CloneNotSupportedException
  {
    AbstractPiece[] pieces = new AbstractPiece[30];
    for (int i = 0; i < 30; i++)
    {
      pieces[i] = new IPiece();
    }
    my_test_board = new TetrisBoard(pieces, 5, 5);
  }
  
  /**
   * Tests the default constructor.
   * @throws CloneNotSupportedException if the current piece is not cloneable
   */
  @Test
  public void testTetrisBoard() throws CloneNotSupportedException
  {
    final TetrisBoard testboard = new TetrisBoard();
    assertEquals("The board is not the correct width: 10", 
                 10, 
                 testboard.getState().length);
    assertEquals("The board is not the correct height: 24 (4 offset)", 
                 24, 
                 testboard.getState()[0].length);
    final boolean[][] state = testboard.getState();
    for (int i = 0; i < 10; i++)
    {
      for (int j = 0; j < 24; j++)
      {
        assertFalse("The board should be empty", state[i][j]);
      }
    }
    assertNotNull("There should be a starting piece", my_test_board.getCurrentPiece());
  }

  /**
   * Tests the parameterized constructor.
   * @throws CloneNotSupportedException exception if pieces aren't cloneable
   */
  @Test
  public void testTetrisBoardIntInt() throws CloneNotSupportedException
  {
    final TetrisBoard parameterized_test_board = new TetrisBoard(5, 5);
    assertNotNull("There should be a starting piece", 
                   parameterized_test_board.getCurrentPiece());
    assertEquals("The width should match the parameter 5", 
                 5, parameterized_test_board.getState().length);
    assertEquals("The height should be 9 (parameter 5 + 4 offset)", 
                 9, parameterized_test_board.getState()[0].length);
  }


  /**
   * Tests that tick() properly updates the board. 
   * @throws CloneNotSupportedException exception if pieces aren't cloneable
   */
  @Test
  public void testTick() throws CloneNotSupportedException
  {
    my_test_board.tick();
    assertEquals("The current piece should have moved down", 
                 1, my_test_board.getCurrentPiece().getY());
    for (int i = 0; i < 5; i++)
    {
      my_test_board.down();
    }
    my_test_board.tick();
    //one IPiece should be frozen in the third column
    assertTrue("The current piece should have been frozen", 
               my_test_board.getState()[2][8] && 
               my_test_board.getState()[2][7] && 
               my_test_board.getState()[2][6] &&
               my_test_board.getState()[2][5]);
    assertNotNull("my_upcoming_piece should have been replaced", 
                  my_test_board.getCurrentPiece());
  }

  /**
   * Tests the down() method.
   * @throws CloneNotSupportedException exception if pieces aren't cloneable.
   */
  @Test
  public void testDown() throws CloneNotSupportedException
  {
    my_test_board.down();
    assertEquals("The piece does not have the expected y coordinate", 
                 1, my_test_board.getCurrentPiece().getY());
    //freezing is tested in the tick() test, which simply calls down()
  }

  /**
   * tests the left() method.
   * @throws CloneNotSupportedException exception if pieces aren't cloneable
   */
  @Test
  public void testLeft() throws CloneNotSupportedException
  {
    my_test_board.left();
    assertEquals("The piece should have moved left 1", 
                 1, my_test_board.getCurrentPiece().getX());
    for (int i = 0; i < 4; i++)
    {
      my_test_board.left();

    }
    assertEquals("The piece should not have moved off the board", 
                 -1, my_test_board.getCurrentPiece().getX());
    //-1 is fine, because a vertical IPiece is to the far left of the 4x4 array
  }

  /**
   * Tests the right() method.
   * @throws CloneNotSupportedException exception if pieces aren't clnoeable;
   */
  @Test
  public void testRight() throws CloneNotSupportedException
  {
    my_test_board.right();
    assertEquals("The piece should have moved to the right", 
                 3, my_test_board.getCurrentPiece().getX());
    for (int i = 0; i < 2; i++)
    {
      my_test_board.right();
    }
    assertEquals("The piece should not have moved off the board.", 
                 4, my_test_board.getCurrentPiece().getX());
  }

  /**
   * Tests the rotate() method.
   * @throws CloneNotSupportedException exception if pieces aren't cloneable
   */
  @Test
  public void testRotate() throws CloneNotSupportedException
  {
    //The piece should not be able to rotate while in its initial position, bounds
    final AbstractPiece piece1 = my_test_board.getCurrentPiece(); //pre rotation
    my_test_board.rotate();
    final AbstractPiece piece2 = my_test_board.getCurrentPiece(); //post rotation
    assertArrayEquals("The piece should not have rotated", 
                      piece1.getState(), piece2.getState());
    for (int i = 0; i < 2; i++)
    {
      my_test_board.left();
    }
    my_test_board.rotate();
    
    final boolean[][] expectedstate = {{false, false, false, false}, 
                                       {true, true, true, true}, 
                                       {false, false, false, false}, 
                                       {false, false, false, false}};
    assertArrayEquals("the piece should have rotated", expectedstate, 
                      my_test_board.getCurrentPiece().getState());
    
    for (int i = 0; i < 8; i++)
    {
      my_test_board.down(); //should freeze the piece at the bottom left
    }
    for (int i = 0; i < 2; i++)
    {
      my_test_board.left();
    }
    my_test_board.rotate(); //should place this piece directly above the first one
    
    for (int i = 0; i < 6; i++)
    {
      my_test_board.down(); //place piece 2 squares above first, should not be able to rotate
    }
    my_test_board.rotate(); //piece should remain horizontal (collision)
    assertArrayEquals("The piece should not have rotated", 
                         expectedstate, my_test_board.getCurrentPiece().getState());
  }
  
  /**
   * Tests getState() to see that shallow copies are not returned.  
   */
  @Test
  public void testGetState()
  {
    assertNotSame("The state should not be a shallow copy", 
                  my_test_board.getState(), my_test_board.getState());
  }

  /**
   * Tests to see clones, not object references are returned.  
   * @throws CloneNotSupportedException exception if pieces aren't cloneable
   */
  @Test
  public void testGetCurrentPiece() throws CloneNotSupportedException
  {
    assertNotSame("This method should not return references to the piece", 
                  my_test_board.getCurrentPiece(), my_test_board.getCurrentPiece());
  }

  /**
   * Tests the toString() method.
   * @throws CloneNotSupportedException exception if pieces aren't cloneable
   */
  @Test
  public void testToString() throws CloneNotSupportedException
  {
    
    assertEquals("The strings do not match", 
                 "     \n     \n     \n     \n     \n", 
                 my_test_board.toString());
    for (int i = 0; i < 4; i++)
    {
      my_test_board.down();
    }
    assertEquals("The strings do not match", 
                 "  x  \n  x  \n  x  \n  x  \n     \n", 
                 my_test_board.toString());

  }
}
